package org.easy.eao.spring.jpa;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.sql.Date;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceException;
import javax.persistence.Query;
import javax.persistence.TemporalType;

import org.apache.commons.lang.StringUtils;
import org.easy.eao.annotations.Named;
import org.easy.eao.annotations.Paging;
import org.easy.eao.annotations.jpa.Temporal;
import org.easy.eao.exception.ActionInitializeException;
import org.easy.eao.exception.ReturnTypeException;
import org.easy.eao.support.Page;
import org.easy.eao.utils.Parameter;
import org.easy.eao.utils.ReflectUtils;
import org.springframework.orm.jpa.JpaCallback;

/**
 * 分页存取动作实现类
 * @author Frank
 */
public class PagingAction extends JpaActionSupport<Paging> {
	
	private static final String DEFAULT_NAMEDQUERY_SUFFIX = "[count]";
	private static final String HQL_REGEX = "[f|F][r|R][o|O][m|M] ";

	/**
	 * 分页查询信息类
	 * 
	 * @author Frank
	 */
	private class PagingQueryInfo {
		/** 默认页码 */
		private int defaultPage;
		/** 默认分页容量 */
		private int defaultSize;
		/** 是否使用命名查询 */
		private boolean named;
		/** 查询语句标识(HQL/查询名) */
		private String findQuery;
		/** 统计语句标识(HQL/查询名) */
		private String countQuery;
	}

	/**
	 * 分页查询对象
	 * 
	 * @author Frank
	 */
	private class PagingQuery {
		private Query find;
		private Query count;
		private int page;
		private int size;
	}

	/** 页码索引值 */
	private Integer pageIdx;
	/** 页容量索引值 */
	private Integer sizeIdx;
	/** 位置参数 */
	private List<Parameter> indexParams = new ArrayList<Parameter>(0);
	/** 命名参数 */
	private List<Parameter> namedParams = new ArrayList<Parameter>(0);
	/** 分页查询信息 */
	private PagingQueryInfo queryInfo = new PagingQueryInfo();

	// 初始化的方法

	@Override
	public void initialize(Annotation annotation, Method method, Class<?> clz) {
		super.initialize(annotation, method, clz);
		if (!Page.class.isAssignableFrom(method.getReturnType()))
			throw new ReturnTypeException("返回类型必须是 Page 类型");
		initizlizePagingQueryInfo();
		initizlizeParameters();
		if (pageIdx == null && queryInfo.defaultPage == 0)
			throw new ActionInitializeException("缺少页码参数设置");
		if (sizeIdx == null && queryInfo.defaultSize == 0)
			throw new ActionInitializeException("缺少分页容量参数设置");
	}

	/**
	 * 初始化分页查询信息
	 */
	private void initizlizePagingQueryInfo() {
		// 获取默认分页数据
		queryInfo.defaultPage = this.annotation.page();
		queryInfo.defaultSize = this.annotation.size();
		// 获取查询设置
		org.easy.eao.annotations.Query query = this.annotation.query();
		if (StringUtils.isBlank(query.named())) {
			queryInfo.findQuery = query.query();
		} else {
			queryInfo.findQuery = query.named();
			queryInfo.named = true;
		}
		// 获取统计设置
		org.easy.eao.annotations.Query count = this.annotation.count();
		if (queryInfo.named) {
			if (StringUtils.isBlank(count.named()))
				queryInfo.countQuery = queryInfo.findQuery + DEFAULT_NAMEDQUERY_SUFFIX;
			else
				queryInfo.countQuery = count.named();
		} else {
			if (StringUtils.isBlank(count.query()))
				queryInfo.countQuery = getDefaultCountHQL(queryInfo.findQuery);
			else
				queryInfo.countQuery = count.query();
		}
	}

	/**
	 * 获取默认统计语句
	 * 
	 * @param query
	 * @return
	 */
	private String getDefaultCountHQL(String query) {
		String[] s = query.split(HQL_REGEX);
		return "select count(*) from " + s[1];
	}

	/**
	 * 初始化查询参数设置
	 * 
	 * @param method
	 */
	private void initizlizeParameters() {
		Parameter[] parameters = ReflectUtils.getParameter(method);
		for (Parameter p : parameters) {
			if (p.hasAnnotation(Paging.Number.class)) {
				pageIdx = p.getIndex();
				continue;
			}
			if (p.hasAnnotation(Paging.Size.class)) {
				sizeIdx = p.getIndex();
				continue;
			}
			if (p.hasAnnotation(Named.class)) {
				namedParams.add(p);
				continue;
			}
			indexParams.add(p);
		}
	}

	// 执行时的方法

	@Override
	@SuppressWarnings({ "unchecked", "rawtypes" })
	public Object execute(final Object[] parameters) {
		return getJpaTemplate().execute(new JpaCallback() {
			@Override
			public Object doInJpa(EntityManager em) throws PersistenceException {
				PagingQuery query = createPagingQuery(em, parameters);
				fittingQuery(query, parameters);
				return executeQuery(query, method);
			}
		});
	}

	/**
	 * 创建分页查询对象
	 * 
	 * @param session
	 *            当前的 Session
	 * @return
	 */
	private PagingQuery createPagingQuery(EntityManager em, Object[] parameters) {
		PagingQuery result = new PagingQuery();
		// 获取查询对象
		if (queryInfo.named) {
			result.find = em.createNamedQuery(queryInfo.findQuery);
			result.count = em.createNamedQuery(queryInfo.countQuery);
		} else {
			result.find = em.createQuery(queryInfo.findQuery);
			result.count = em.createQuery(queryInfo.countQuery);
		}
		// 获取分页实际值
		if (pageIdx != null)
			result.page = parameters[pageIdx] == null ? 
					queryInfo.defaultPage : (Integer) parameters[pageIdx];
		else
			result.page = queryInfo.defaultPage;
		if (sizeIdx != null)
			result.size = parameters[sizeIdx] == null ?
					queryInfo.defaultSize : (Integer) parameters[sizeIdx];
		else
			result.size = queryInfo.defaultSize;
		return result;
	}

	/**
	 * 设置查询参数
	 * 
	 * @param query
	 * @param parameters
	 */
	protected void fittingQuery(PagingQuery query, Object[] parameters) {
		for (int i = 0; i < indexParams.size(); i++) {
			Parameter p = indexParams.get(i);
			if (p.hasAnnotation(Temporal.class)) {
				Temporal anno = p.getAnnotation(Temporal.class);
				TemporalType type = TemporalType.valueOf(anno.value());
				if (parameters[p.getIndex()] instanceof Date) {
					query.find.setParameter(i + 1, (Date) parameters[p.getIndex()], type);
					query.count.setParameter(i + 1, (Date) parameters[p.getIndex()], type);
				} else {
					query.find.setParameter(i + 1, (Calendar) parameters[p.getIndex()], type);
					query.count.setParameter(i + 1, (Calendar) parameters[p.getIndex()], type);
				}
			} else {
				query.find.setParameter(i + 1, parameters[p.getIndex()]);
				query.count.setParameter(i + 1, parameters[p.getIndex()]);
			}
		}
		for (Parameter p : namedParams) {
			Named named = p.getAnnotation(Named.class);
			if (p.hasAnnotation(Temporal.class)) {
				Temporal anno = p.getAnnotation(Temporal.class);
				TemporalType type = TemporalType.valueOf(anno.value());
				if (parameters[p.getIndex()] instanceof Date) {
					query.find.setParameter(named.value(), (Date) parameters[p.getIndex()], type);
					query.count.setParameter(named.value(), (Date) parameters[p.getIndex()], type);
				} else {
					query.find.setParameter(named.value(), (Calendar) parameters[p.getIndex()], type);
					query.count.setParameter(named.value(), (Calendar) parameters[p.getIndex()], type);
				}
			} else {
				query.find.setParameter(named.value(), parameters[p.getIndex()]);
				query.count.setParameter(named.value(), parameters[p.getIndex()]);
			}
		}
		query.find.setFirstResult(Page.getStartOfPage(query.page, query.size));
		query.find.setMaxResults(query.size);
	}

	/**
	 * 执行查询
	 * 
	 * @param query
	 * @param method
	 * @return
	 */
	@SuppressWarnings({ "unchecked", "rawtypes" })
	protected Page<?> executeQuery(PagingQuery query, Method method) {
		long total = ((Long) query.count.getSingleResult()).longValue();
		int first = Page.getStartOfPage(query.page, query.size);
		if (total < first)
			return new Page(first, total, query.size, new ArrayList());
		return new Page(first, total, query.size, query.find.getResultList());
	}
}
